Published on

solidity

Authors

https://remix.ethereum.org/ 在线调试IDE--remix

合约基本结构

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract Quiz1{
    // add something here
    string public _string = "WTF";
}

值类型

Boolean布尔型

bool public _bool = true;

Integers整型

int public _int = -1;
uint public _uint = 1;
uint256 public _number = 210980123;

int和uint和存储整型,最多存储256位即32字节的数据。

Address地址类型

address存储20bytes(40位十六进制数,160bit)的以太坊地址。 address payable与address相同,不过它具有两个成员函数,transfer和send,用来转移ETH。

address public addr1 = 0x1234567890123456789012345678901234567890;
address public addr2 = address(0x123);
address payable public _payable = payable(addr1);

function getBalance(address _addr) public view returns (uint256) {
    return _addr.balance;
}

// 向_payabled地址转入1Wei
// 如果不加require判断,msg.value不管多大,剩余的都会转入合约地址,需要合约做退款处理
function trans() public payable {
    require(msg.value >= 1, "Please enter a right vale");
    _payable.transfer(1);
}

Fixed-size byte arrays定长字节数组

字节数组类型包括 bytes1,bytes8,bytes32 等等,最大的是bytes32即256bit。

bytes则是动态大小的字节数组。与string的主要区别是,string存的是utf字符串,bytes是字节数据。

    bytes32 public _byte32 = "dd";
    bytes4 public _byte = 0x01010101;
    bytes1 public _bytes1 = _byte32[0];
    bytes public _bytes = "123123123";
    bytes public _bytes2 = hex"fe132131";

函数

function <function name>(<parameter types>) [internal|external] [pure|view|payable] [returns (<return types>)]

可见性修饰符

  • 函数需指定可见性,没有默认值
  • private,只能从内部访问,不能继承。
  • external,只能被外部合约访问,不过可以通过this.f()被内部调用,处理大型数据结构更省gas,因为数据不会被复制到内存中。
  • internal,只能被内部访问,可以继承。
  • public,private,internal还可以修饰状态变量,public变量会自动生成同名的getter函数。
  • 状态变量默认的可见性为internal

行为修饰符

当函数被定义为payable时,可以通过msg.value发送ETH。

pure 意味着纯计算函数,没有更改合约内部变量,也没有读取。

具体见之前的博客有写,纯计算函数

view 可以理解为只读,读取了合约内部状态,包括变量,全局变量。

pure和view从外部调用时,不消耗gas,合约内部调用会消耗gas。

返回值

返回多个值

    function returnValues() pure public returns(uint, address, uint[3] memory) {
        return (1, address(0x123),[uint(1),2,3]);
    }

存储位置

有三种存储位置,分别是storage,memory和calldata。

storage存储在链上类似硬盘,memory和calldata类似内存,临时存储在内存中。

创建引用时,storage的引用会改变原数组,memory不会改变原数组。

    int[] public storageVar = [int(0),1,3,4];

    function testStorage()  public returns(int[] memory, int[] memory) {
        int[] storage _storageVar = storageVar; //创建一个storage的引用,跟直接用全局变量没区别。
        _storageVar[0] = 100;
        storageVar[1] = 200;

        int[] memory _memoryVar = storageVar; //直接拷贝了一份到
        _memoryVar[2] = 300;

        return (_storageVar, _memoryVar);
    }

storage

状态变量默认就是storage类型的,储存在链上。

memory

函数的参数和临时变量一般都是memory

calldata

calldata与memory很像,都不存在链上,但是calldata不能修改,一般用于函数参数。

    function testCalldata(string[] calldata a) public pure returns(string[] calldata){
        // cant modify, unless is a memory variable
        //a[0] = "a";
        return a;
    }

变量作用域

状态变量 State variable

类似其他语言中的全局变量

contract MyVariable{
    uint public score = 999;
    string public name = "Meow";
    string public solgan = "Make America meow again!";
}

局部变量 Local variable

跟其他语言中的局部变量类似,在函数中定义,作用域就只在函数中。

全局变量 Global variable

英文叫全局变量,感觉更像环境变量,在整个evm环境中存在的变量。或者说它的全局范围在整个evm中。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract GlobalVariablesGetter {
    struct GlobalVars {
        uint256 blockNumber;
        uint256 blockTimestamp;
        uint256 blockChainid;
        address blockCoinbase;
        uint256 blockDifficulty;
        uint256 blockPrevrandao;
        uint256 blockGaslimit;
        address msgSender;
        uint256 msgValue;
        uint256 txGasprice;
        address txOrigin;
        uint256 gasLeft;
        uint256 thisBalance;
    }

    function getGlobalVariables() public payable  returns (GlobalVars memory) {
        return GlobalVars({
            blockNumber: block.number,
            blockTimestamp: block.timestamp,
            blockChainid: block.chainid,
            blockCoinbase: block.coinbase,
            blockDifficulty: block.difficulty,
            blockPrevrandao: block.prevrandao,
            blockGaslimit: block.gaslimit,
            msgSender: msg.sender,
            msgValue: msg.value,
            txGasprice: tx.gasprice,
            txOrigin: tx.origin,
            gasLeft: gasleft(),
            thisBalance: address(this).balance
        });
    }

    function getTypeInfo() public pure returns (uint256, uint256, int256, int256) {
        return (
            type(uint256).min,
            type(uint256).max,
            type(int256).min,
            type(int256).max
        );
    }
}

block.difficulty 在转PoS后已经被 block.prevrandao 替代了,返回的是beacon chain随机数,在remix的vm里不存在。

Array 数组

分为定长数组,和可变数组。 定长数组,通过下标赋值,可变数组通过push,pop赋值。

而局部变量的数组,必须定义成memory,而且需指定长度。

通过length方法可以得到数组的长度。

// SPDX-License-Identifier: MyRule
pragma solidity ^0.8.0;

contract Array {
    //Fixed Array
    uint[3] public _uintArr;
    address[100] public _addrArr;

    //variable-length Array
    uint[] public _uintArr2;
    address[] public _addrArr2;

    bytes public _bytesArr;
    bytes1[] public _bytes1Arr;

    function testArray() public {
        _uintArr[0] = 1;
        _addrArr[0] = address(0x1);

        _uintArr2.push(1);
        _addrArr2.push(address(0x2));

        _bytesArr.push(0x12);

        uint[] memory tmp_uint_arr = new uint[](5);
        tmp_uint_arr[0] = 1;

        uint[5] memory tmp_uint_arr2;
        tmp_uint_arr2[0] = 1;
    }
}

Eample Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract Mytest{
    // add something here
    bool public _bool = true;
    int16 public _int16 = 9999;

    string public _string = "hello";
    bytes public _bytes = hex"feab0123";

    bytes32 public _byte32 = "dd";
    bytes1 public _byte1 = _byte32[0];
    address public _address = address(0x123);
    address payable public _payable = payable(0x0);

    // make DirectionSet contain East, South, West, North.
    enum DirectionSet {East, South, West, North}

    function trans() public payable {
        require(msg.value >= 1, "Please send enough ETH");
      _payable.transfer(1);
    }

    function transWithRefund() public payable {
        require(msg.value >= 1, "Please send enough ETH");

        if(msg.value > 1){
            payable(msg.sender).transfer(msg.value - 1);
        }

      _payable.transfer(1);
    }

    function getEnumDirectionSet() public pure returns (DirectionSet) {
        return DirectionSet.North;
    }

    function getBalance() public view returns (uint256) {
        return _payable.balance;
    }

    function getContractBalance() public view returns (uint256) {
        return address(this).balance;
    }

    function returnValues() pure public returns(uint, address, uint[3] memory) {
        return (1, address(0x123),[uint(1),2,3]);
    }

    function testCalldata(string[] calldata a) public pure returns(string[] calldata){
        // cant modify, unless is a memory variable
        //a[0] = "a";
        return a;
    }

    int[] public storageVar = [int(0),1,3,4];

    function testStorage()  public returns(int[] memory, int[] memory) {
        int[] storage _storageVar = storageVar; //创建一个storage的引用,跟直接用全局变量没区别。
        _storageVar[0] = 100;
        storageVar[1] = 200;

        int[] memory _memoryVar = storageVar; //直接拷贝了一份到
        _memoryVar[2] = 300;

        return (_storageVar, _memoryVar);
    }

}